package com.example.common.tools

import android.content.Context
import java.text.SimpleDateFormat
import android.os.Looper
import android.widget.Toast
import android.os.Build
import android.content.pm.PackageManager
import android.os.Environment
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
import java.io.PrintWriter
import java.util.*
import kotlin.collections.HashMap
import java.io.StringWriter as StringWriter1


class CrashHandler : Thread.UncaughtExceptionHandler {

    companion object {
        val TAG = "CrashHandler"

        // CrashHandler实例
        val instance by lazy(LazyThreadSafetyMode.NONE) {
            CrashHandler()
        }
    }

    // 系统默认的UncaughtException处理类
    private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null

    // 程序的Context对象
    private var mContext: Context? = null

    // 用来存储设备信息和异常信息
    private val infos = HashMap<String, String>()

    // 用于格式化日期,作为日志文件名的一部分
    private val formatter = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss")

    private val path = "/sdcard/aithu/log"// 日志文件在sdcard中的路径

    var listener: CrashListener? = null

    var isSaveCrash = false

    /**
     * 初始化
     *
     * @param context
     */
    fun init(context: Context): CrashHandler {
        mContext = context
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this)
        return this
    }

    override fun uncaughtException(thread: Thread?, ex: Throwable?) {
        if (mDefaultHandler == null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            android.os.Process.killProcess(android.os.Process.myPid())
            return
        }
        handleException(ex)
    }

    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private fun handleException(ex: Throwable?) {
        if (ex == null) {
            android.os.Process.killProcess(android.os.Process.myPid())
            return
        }
        LogUtils.eLog(TAG, "异常捕获 =  ${ex!!.message}")
        // 使用Toast来显示异常信息
        object : Thread() {
            override fun run() {
                Looper.prepare()
                Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show()
                Looper.loop()
            }
        }.start()
        // 收集设备参数信息
        collectDeviceInfo(mContext!!)
        var exMsg = if (ex.message == null) "null" else ex.message
        infos["throw"] = exMsg!!
        var throwJson = JsonUtil.instance.mapToJson(infos)
        listener?.onCrash(throwJson)
        //上传闪退日志
//        PgyerSDKManager.reportException(java.lang.Exception(exMsg))
        // 保存日志文件
        if (isSaveCrash) {
            val saveCrashInfo2File = saveCrashInfo2File(ex!!)
            LogUtils.eLog(TAG,"异常文件位置  =  $saveCrashInfo2File")
        }

        android.os.Process.killProcess(android.os.Process.myPid())

    }


    /**
     * 收集设备参数信息
     *
     * @param ctx
     */
    fun collectDeviceInfo(ctx: Context) {
        try {
            val pm = ctx.packageManager
            val pi = pm.getPackageInfo(ctx.packageName, PackageManager.GET_ACTIVITIES)
            if (pi != null) {
                val versionName = if (pi.versionName == null) "null" else pi.versionName
                val versionCode = pi.versionCode.toString() + ""
                infos["versionName"] = versionName
                infos["versionCode"] = versionCode
            }
        } catch (e: PackageManager.NameNotFoundException) {
            LogUtils.e(TAG, "an error occured when collect package info = $e")
        }

        val fields = Build::class.java.declaredFields
        for (field in fields) {
            try {
                field.isAccessible = true
                infos[field.name] = field.get(null).toString()
                LogUtils.d(TAG, field.name + " : " + field.get(null))
            } catch (e: Exception) {
                LogUtils.e(TAG, "an error occured when collect crash info = $e")
            }

        }
    }


    /**
     * 保存错误信息到文件中
     *
     * @param ex
     * @return 返回文件名称,便于将文件传送到服务器
     */
    fun saveCrashInfo2File(ex: Throwable): String? {

        val sb = StringBuffer()
        for ((key, value) in infos) {
            sb.append("$key=$value\n")
        }

        val writer = StringWriter1()
        val printWriter = PrintWriter(writer)
        ex.printStackTrace(printWriter)
        var cause: Throwable? = ex.cause
        while (cause != null) {
            cause.printStackTrace(printWriter)
            cause = cause.cause
        }
        printWriter.close()
        val result = writer.toString()
        sb.append(result)
        try {
            val timestamp = System.currentTimeMillis()
            val time = formatter.format(Date())
            val fileName = "crash-$time-${timestamp}Log.txt"
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                val dir = File(path)
                if (!dir.exists()) {
                    dir.mkdirs()
                }
                val fos = FileOutputStream(path + fileName)
                fos.write(sb.toString().toByteArray())
                fos.close()
            }
            return fileName
        } catch (e: Exception) {
            LogUtils.e(TAG, "an error occured while writing file... $e")
        }
        return null
    }


    interface CrashListener {
        fun onCrash(crash: String)
    }

}

